/********************************************************************************/
/* RAZE Z80 emulator                                                            */
/* Heavily based on CZ80 emulator by Stephane Dallongueville                    */
/********************************************************************************/

#if CZ80_USE_JUMPTABLE
    goto *JumpTableED[Opcode];
#else
switch (Opcode)
{
#endif

    /* ILLEGAL */

    OPED(0x00):
    OPED(0x01):
    OPED(0x02):
    OPED(0x03):
    OPED(0x04):
    OPED(0x05):
    OPED(0x06):
    OPED(0x07):
    OPED(0x08):
    OPED(0x09):
    OPED(0x0a):
    OPED(0x0b):
    OPED(0x0c):
    OPED(0x0d):
    OPED(0x0e):
    OPED(0x0f):
    OPED(0x10):
    OPED(0x11):
    OPED(0x12):
    OPED(0x13):
    OPED(0x14):
    OPED(0x15):
    OPED(0x16):
    OPED(0x17):
    OPED(0x18):
    OPED(0x19):
    OPED(0x1a):
    OPED(0x1b):
    OPED(0x1c):
    OPED(0x1d):
    OPED(0x1e):
    OPED(0x1f):
    OPED(0x20):
    OPED(0x21):
    OPED(0x22):
    OPED(0x23):
    OPED(0x24):
    OPED(0x25):
    OPED(0x26):
    OPED(0x27):
    OPED(0x28):
    OPED(0x29):
    OPED(0x2a):
    OPED(0x2b):
    OPED(0x2c):
    OPED(0x2d):
    OPED(0x2e):
    OPED(0x2f):
    OPED(0x30):
    OPED(0x31):
    OPED(0x32):
    OPED(0x33):
    OPED(0x34):
    OPED(0x35):
    OPED(0x36):
    OPED(0x37):
    OPED(0x38):
    OPED(0x39):
    OPED(0x3a):
    OPED(0x3b):
    OPED(0x3c):
    OPED(0x3d):
    OPED(0x3e):
    OPED(0x3f):
    OPED(0xbc):
    OPED(0xbd):
    OPED(0xbe):
    OPED(0xbf):
    OPED(0xc0):
    OPED(0xc1):
    OPED(0xc2):
    OPED(0xc3):
    OPED(0xc4):
    OPED(0xc5):
    OPED(0xc6):
    OPED(0xc7):
    OPED(0xc8):
    OPED(0xc9):
    OPED(0xca):
    OPED(0xcb):
    OPED(0xcc):
    OPED(0xcd):
    OPED(0xce):
    OPED(0xcf):
    OPED(0xd0):
    OPED(0xd1):
    OPED(0xd2):
    OPED(0xd3):
    OPED(0xd4):
    OPED(0xd5):
    OPED(0xd6):
    OPED(0xd7):
    OPED(0xd8):
    OPED(0xd9):
    OPED(0xda):
    OPED(0xdb):
    OPED(0xdc):
    OPED(0xdd):
    OPED(0xde):
    OPED(0xdf):
    OPED(0xe0):
    OPED(0xe1):
    OPED(0xe2):
    OPED(0xe3):
    OPED(0xe4):
    OPED(0xe5):
    OPED(0xe6):
    OPED(0xe7):
    OPED(0xe8):
    OPED(0xe9):
    OPED(0xea):
    OPED(0xeb):
    OPED(0xec):
    OPED(0xed):
    OPED(0xee):
    OPED(0xef):
    OPED(0xf0):
    OPED(0xf1):
    OPED(0xf2):
    OPED(0xf3):
    OPED(0xf4):
    OPED(0xf5):
    OPED(0xf6):
    OPED(0xf7):
    OPED(0xf8):
    OPED(0xf9):
    OPED(0xfa):
    OPED(0xfb):
    OPED(0xfc):
    OPED(0xfd):
    OPED(0xfe):
    OPED(0xff):
    OPED(0x77):
    OPED(0x7f):
    OPED(0x80):
    OPED(0x81):
    OPED(0x82):
    OPED(0x83):
    OPED(0x84):
    OPED(0x85):
    OPED(0x86):
    OPED(0x87):
    OPED(0x88):
    OPED(0x89):
    OPED(0x8a):
    OPED(0x8b):
    OPED(0x8c):
    OPED(0x8d):
    OPED(0x8e):
    OPED(0x8f):
    OPED(0x90):
    OPED(0x91):
    OPED(0x92):
    OPED(0x93):
    OPED(0x94):
    OPED(0x95):
    OPED(0x96):
    OPED(0x97):
    OPED(0x98):
    OPED(0x99):
    OPED(0x9a):
    OPED(0x9b):
    OPED(0x9c):
    OPED(0x9d):
    OPED(0x9e):
    OPED(0x9f):
    OPED(0xa4):
    OPED(0xa5):
    OPED(0xa6):
    OPED(0xa7):
    OPED(0xac):
    OPED(0xad):
    OPED(0xae):
    OPED(0xaf):
    OPED(0xb4):
    OPED(0xb5):
    OPED(0xb6):
    OPED(0xb7):
        goto OP_NOP;

    OPED(0x43): /* LD   (w),BC */
        data = pzBC;
        goto OP_LD_mNN_xx;

    OPED(0x53): /* LD   (w),DE */
        data = pzDE;
        goto OP_LD_mNN_xx;

    OPED(0x63): /* LD   (w),HL */
        data = pzHL;
        goto OP_LD_mNN_xx;

    OPED(0x73): /* LD   (w),SP */
        data = pzSP;
        goto OP_LD_mNN_xx;

    OPED(0x4b): /* LD   BC,(w) */
        data = pzBC;
        goto OP_LD_xx_mNN;

    OPED(0x5b): /* LD   DE,(w) */
        data = pzDE;
        goto OP_LD_xx_mNN;

    OPED(0x6b): /* LD   HL,(w) */
        data = pzHL;
        goto OP_LD_xx_mNN;

    OPED(0x7b): /* LD   SP,(w) */
        data = pzSP;
        goto OP_LD_xx_mNN;


    OPED(0x47): /* LD   I,A */
        zI = zA;
        RET(5)

    OPED(0x4f): /* LD   R,A */
        zR = zA - ((cpu.CycleToDo - CCnt) / 4);
        zR2 = zA & 0x80;
        RET(5)

    OPED(0x57): /* LD   A,I */
    {
        u8 F;

        zA = zI;
        F = zF & CZ80_CF;
        F |= zA & (CZ80_SF | CZ80_YF | CZ80_XF);
        F |= zIFF2;
        if (!zA) F |= CZ80_ZF;
        zF = F;
        RET(5)
    }

    OPED(0x5f): /* LD   A,R */
    {
        u8 F;

        zA = zR2 + ((zR + ((cpu.CycleToDo - CCnt) / 4)) & 0x7F);
        F = zF & CZ80_CF;
        F |= zA & (CZ80_SF | CZ80_YF | CZ80_XF);
        F |= zIFF2;
        if (!zA) F |= CZ80_ZF;
        zF = F;
        RET(5)
    }

    OPED(0x5c): /* NEG */
    OPED(0x54): /* NEG */
    OPED(0x4c): /* NEG */
    OPED(0x44): /* NEG */
    OPED(0x64): /* NEG */
    OPED(0x6c): /* NEG */
    OPED(0x74): /* NEG */
    OPED(0x7c): /* NEG */
    {
        u32 val;
        u32 res;

        val = zA;
        res = 0 - val;
        zF = SZXY[res & 0xFF] |                     /* S/Z/X/Y flag */
            ((res ^ val) & CZ80_HF) |               /* H flag */
            (((val & res) & 0x80) >> 5) |           /* V flag */
            ((res >> 8) & CZ80_CF) | CZ80_NF;       /* C/N flag */
        zA = res;
        RET(4)
    }


    OPED(0x67): /* RRD  (HL) */
    {
        u32 adr;
        u8 src;

        PRE_IO
        adr = zHL;
        READ_BYTE(adr, src)
        WRITE_BYTE(adr, (src >> 4) | (zA << 4))
        zA = (zA & 0xF0) | (src & 0x0F);
        zF = SZXYP[zA] | (zF & CZ80_CF);
        POST_IO
        RET(14)
    }

    OPED(0x6f): /* RLD  (HL) */
    {
        u32 adr;
        u8 src;

        PRE_IO
        adr = zHL;
        READ_BYTE(adr, src)
        WRITE_BYTE(adr, (src << 4) | (zA & 0x0F))
        zA = (zA & 0xF0) | (src >> 4);
        zF = SZXYP[zA] | (zF & CZ80_CF);
        POST_IO
        RET(14)
    }


    {
        u32 src;
        u32 res;

    OPED(0x7a): /* ADC  HL,SP */
        src = zSP;
        goto OP_ADC_HL;

    OPED(0x4a): /* ADC  HL,BC */
    OPED(0x5a): /* ADC  HL,DE */
    OPED(0x6a): /* ADC  HL,HL */
        src = zR16((Opcode >> 4) & 3);

OP_ADC_HL:
        res = zHL + src + (zF & CZ80_CF);
        zF = (((src ^ zHL ^ res) >> 8) & CZ80_HF) |                     /* H flag */
            (((src ^ zHL ^ 0x8000) & (src ^ res) & 0x8000) >> 13) |     /* V flag */
            ((res >> 8) & (CZ80_SF | CZ80_XF | CZ80_YF)) |              /* S/X/Y flag */
            ((res >> 16) & CZ80_CF) |                                   /* C flag */
            ((res & 0xFFFF) ? 0 : CZ80_ZF);                             /* Z flag */
        zHL = res;
        RET(11)


    OPED(0x72): /* SBC  HL,SP */
        src = zSP;
        goto OP_SBC_HL;

    OPED(0x42): /* SBC  HL,BC */
    OPED(0x52): /* SBC  HL,DE */
    OPED(0x62): /* SBC  HL,HL */
        src = zR16((Opcode >> 4) & 3);

OP_SBC_HL:
        res = zHL - src + (zF & CZ80_CF);
#if CZ80_DEBUG
        zF = (((src ^ zHL ^ res) >> 8) & CZ80_HF) | CZ80_NF |           /* H/N flag */
            (((src ^ zHL) & (zHL ^ res) & 0x8000) >> 13) |              /* V flag */
            ((res >> 8) & CZ80_SF) |                                    /* S flag */
            ((res >> 16) & CZ80_CF) |                                   /* C flag */
            ((res & 0xFFFF) ? 0 : CZ80_ZF);                             /* Z flag */
#else
        zF = (((src ^ zHL ^ res) >> 8) & CZ80_HF) | CZ80_NF |           /* H/N flag */
            (((src ^ zHL) & (zHL ^ res) & 0x8000) >> 13) |              /* V flag */
            ((res >> 8) & (CZ80_SF | CZ80_XF | CZ80_YF)) |              /* S/X/Y flag */
            ((res >> 16) & CZ80_CF) |                                   /* C flag */
            ((res & 0xFFFF) ? 0 : CZ80_ZF);                             /* Z flag */
#endif
        zHL = res;
        RET(11)
    }


    {
        u32 res;

    OPED(0x40): /* IN   B,(C) */
    OPED(0x48): /* IN   C,(C) */
    OPED(0x50): /* IN   D,(C) */
    OPED(0x58): /* IN   E,(C) */
    OPED(0x60): /* IN   H,(C) */
    OPED(0x68): /* IN   L,(C) */
    OPED(0x78): /* IN   E,(C) */

        IN(zBC, res);
        zR8((Opcode >> 3) & 7) = res;
        zF = (zF & CZ80_CF) | SZXYP[res];
        RET(8)

    OPED(0x70): /* IN   0,(C) */

        IN(zBC, res);
        zF = (zF & CZ80_CF) | SZXYP[res];
        RET(8)
    }


    {
        u32 src;

    OPED(0x71): /* OUT  (C),0 */
        src = 0;
        goto OP_OUT_mBC;

    OPED(0x51): /* OUT  (C),D */
    OPED(0x41): /* OUT  (C),B */
    OPED(0x49): /* OUT  (C),C */
    OPED(0x59): /* OUT  (C),E */
    OPED(0x61): /* OUT  (C),H */
    OPED(0x69): /* OUT  (C),L */
    OPED(0x79): /* OUT  (C),E */
        src = zR8((Opcode >> 3) & 7);

OP_OUT_mBC:
        OUT(zBC, src);
        RET(8)
    }

    {
        u32 newPC;

    OPED(0x4d): /* RETI */
    OPED(0x5d): /* RETI */
    OPED(0x6d): /* RETI */
    OPED(0x7d): /* RETI */
        if (cpu.RetI) cpu.RetI();

    OPED(0x45): /* RETN; */
    OPED(0x55): /* RETN; */
    OPED(0x65): /* RETN; */
    OPED(0x75): /* RETN; */
        PRE_IO
        POP_16(newPC);
        SET_PC(newPC);
        POST_IO
        zIFF1 = zIFF2;
        CCnt -= 10;
        /* we need to test for interrupt */
        goto Cz80_Check_Int;
    }

    OPED(0x46): /* IM   0 */
    OPED(0x4e): /* IM   0 */
    OPED(0x66): /* IM   0 */
    OPED(0x6e): /* IM   0 */
        zIM = 0;
        RET(4)

    OPED(0x76): /* IM   1 */
    OPED(0x56): /* IM   1 */
        zIM = 1;
        RET(4)

    OPED(0x5e): /* IM   2 */
    OPED(0x7e): /* IM   2 */
        zIM = 2;
        RET(4)


    {
        u8 val;
        u8 F;

    OPED(0xa8): /* LDD */
        PRE_IO
        READ_BYTE(zHL--, val)
        WRITE_BYTE(zDE--, val)
        goto OP_LDX;

    OPED(0xa0): /* LDI */
        PRE_IO
        READ_BYTE(zHL++, val)
        WRITE_BYTE(zDE++, val)

OP_LDX:
#if CZ80_EXACT
        val += zA;
        F = (zF & (CZ80_SF | CZ80_ZF | CZ80_CF)) |
            (val & CZ80_XF) | ((val << 4) & CZ80_YF);
#else
        F = zF & (CZ80_SF | CZ80_ZF | CZ80_YF | CZ80_XF | CZ80_CF);
#endif
        if (--zBC) F |= CZ80_PF;
        zF = F;
        POST_IO
        RET(12)
    }

    {
        u8 val;
        u8 F;

    OPED(0xb8): /* LDDR */
        do
        {
            PRE_IO
            READ_BYTE(zHL--, val)
            WRITE_BYTE(zDE--, val)
            POST_IO
            zBC--;
            CCnt -= 21;
        } while ((zBC) && (CCnt > -4));
        goto OP_LDXR;

    OPED(0xb0): /* LDIR */
        do
        {
            PRE_IO
            READ_BYTE(zHL++, val)
            WRITE_BYTE(zDE++, val)
            POST_IO
            zBC--;
            CCnt -= 21;
        } while ((zBC) && (CCnt > -4));

OP_LDXR:
#if CZ80_EXACT
        val += zA;
        F = (zF & (CZ80_SF | CZ80_ZF | CZ80_CF)) |
            (val & CZ80_XF) | ((val << 4) & CZ80_YF);
#else
        F = zF & (CZ80_SF | CZ80_ZF | CZ80_YF | CZ80_XF | CZ80_CF);
#endif

        if (zBC)
        {
            /* instruction not yet completed... */
            /* we will continu it at next CZ80_Exec */
            zF = F | CZ80_PF;
            zPC -= 2;
            CCnt += 4;
            goto Cz80_Check_Int;
        }

        /* instruction completed... */
        zF = F;
        RET(-(5 + 4))
    }


    {
        u8 val;
        u8 res;
        u8 F;

    OPED(0xa9): /* CPD */
        PRE_IO
        READ_BYTE(zHL--, val)
        goto OP_CPX;

    OPED(0xa1): /* CPI */
        PRE_IO
        READ_BYTE(zHL++, val)

OP_CPX:
        res = zA - val;
#if CZ80_EXACT
        F = (zF & CZ80_CF) | (SZXY[res] & ~(CZ80_YF | CZ80_XF)) |
            ((zA ^ val ^ res) & CZ80_HF) | CZ80_NF;
        if (F & CZ80_HF) res--;
        F |= (res & CZ80_XF) | ((res >> 4) & CZ80_YF);
#else
        F = (zF & CZ80_CF) | SZXY[res] |
            ((zA ^ val ^ res) & CZ80_HF) | CZ80_NF;
#endif
        if (--zBC) F |= CZ80_PF;
        zF = F;
        POST_IO
        RET(12)
    }

    {
        u32 val;
        u32 res;
        u8 F;

    OPED(0xb9): /* CPDR */
        do
        {
            PRE_IO
            READ_BYTE(zHL--, val)
            res = zA - val;
            POST_IO
            zBC--;
            CCnt -= 21;
        } while ((zBC) && (res) && (CCnt > -4));
        goto OP_CPXR;

    OPED(0xb1): /* CPIR */
        do
        {
            PRE_IO
            READ_BYTE(zHL++, val)
            res = zA - val;
            POST_IO
            zBC--;
            CCnt -= 21;
        } while ((zBC) && (res) && (CCnt > -4));

OP_CPXR:
#if CZ80_EXACT
        F = (zF & CZ80_CF) | (SZXY[res] & ~(CZ80_YF | CZ80_XF)) |
            ((zA ^ val ^ res) & CZ80_HF) | CZ80_NF;
        if (F & CZ80_HF) res--;
        F |= (res & CZ80_XF) | ((res >> 4) & CZ80_YF);
#else
        F = (zF & CZ80_CF) | SZXY[res] |
            ((zA ^ val ^ res) & CZ80_HF) | CZ80_NF;
#endif

        if (zBC)
        {
            /* instruction not yet completed... */
            /* we will continu it at next CZ80_Exec */
            zF = F | CZ80_PF;
            zPC -= 2;
            CCnt += 4;
            goto Cz80_Check_Int;
        }

        /* instruction completed... */
        zF = F;
        RET(-(3 + 4))
    }


    {
        u8 val;
#if CZ80_EXACT
        u8 F;
#endif

    OPED(0xaa): /* IND */
        PRE_IO
        IN(zBC, val)
        WRITE_BYTE(zHL--, val)
#if CZ80_EXACT
        if ((((zC - 1) & 0xFF) + val) & 0x100)
        {
            F = CZ80_HF | CZ80_CF;
            goto OP_INX;
        }
        F = 0;
#endif
        goto OP_INX;

    OPED(0xa2): /* INI */
        PRE_IO
        IN(zBC, val)
        WRITE_BYTE(zHL++, val)
#if CZ80_EXACT
        if ((((zC + 1) & 0xFF) + val) & 0x100)
        {
            F = CZ80_HF | CZ80_CF;
            goto OP_INX;
        }
        F = 0;
#endif

OP_INX:
#if CZ80_EXACT
        /* P FLAG isn't correct here ! */
        zF = F | (SZXY[--zB] + ((val >> 6) & CZ80_NF) + (val & CZ80_PF));
#else
        zF = SZXY[--zB] + ((val >> 6) & CZ80_NF);
#endif
        POST_IO
        RET(12)
    }

    {
        u8 val;
#if CZ80_EXACT
        u8 F;
#endif

    OPED(0xba): /* INDR */
        do
        {
            PRE_IO
            IN(zBC, val)
            WRITE_BYTE(zHL--, val)
            POST_IO
            zB--;
            CCnt -= 21;
        } while ((zB) && (CCnt > -4));
#if CZ80_EXACT
        if ((((zC - 1) & 0xFF) + val) & 0x100)
        {
            F = CZ80_HF | CZ80_CF;
            goto OP_INXR;
        }
        F = 0;
#endif
        goto OP_INXR;

    OPED(0xb2): /* INIR */
        do
        {
            PRE_IO
            IN(zBC, val)
            WRITE_BYTE(zHL++, val)
            POST_IO
            zB--;
            CCnt -= 21;
        } while ((zB) && (CCnt > -4));
#if CZ80_EXACT
        if ((((zC + 1) & 0xFF) + val) & 0x100)
        {
            F = CZ80_HF | CZ80_CF;
            goto OP_INXR;
        }
        F = 0;
#endif

OP_INXR:
#if CZ80_EXACT
        /* P FLAG isn't correct here ! */
        zF = F | (SZXY[zB] + ((val >> 6) & CZ80_NF) + (val & CZ80_PF));
#else
        zF = SZXY[zB] + ((val >> 6) & CZ80_NF);
#endif

        if (zB)
        {
            /* instruction not yet completed... */
            /* we will continu it at next CZ80_Exec */
            zPC -= 2;
            CCnt += 4;
            goto Cz80_Check_Int;
        }

        /* instruction completed... */
        RET(-(5 + 4))
    }


    {
        u8 val;
#if CZ80_EXACT
        u8 F;
#endif

    OPED(0xab): /* OUTD */
        PRE_IO
        READ_BYTE(zHL--, val)
        OUT(zBC, val)
        goto OP_OUTX;

    OPED(0xa3): /* OUTI */
        PRE_IO
        READ_BYTE(zHL++, val)
        OUT(zBC, val)

OP_OUTX:
#if CZ80_EXACT
        /* P FLAG isn't correct here ! */
        F = SZXY[--zB] + ((val >> 6) & CZ80_NF) + (val & CZ80_PF);
        if ((val + zL) & 0x100) F |= CZ80_HF | CZ80_CF;
        zF = F;
#else
        zF = SZXY[--zB] + ((val >> 6) & CZ80_NF);
#endif
        POST_IO
        RET(12)
    }


    {
        u8 val;
#if CZ80_EXACT
        u8 F;
#endif

    OPED(0xbb): /* OUTDR */
        do
        {
            PRE_IO
            READ_BYTE(zHL--, val)
            OUT(zBC, val)
            POST_IO
            zB--;
            CCnt -= 21;
        } while ((zB) && (CCnt > -4));
        goto OP_OUTXR;

    OPED(0xb3): /* OUTIR */
        do
        {
            PRE_IO
            READ_BYTE(zHL++, val)
            OUT(zBC, val)
            POST_IO
            zB--;
            CCnt -= 21;
        } while ((zB) && (CCnt > -4));

OP_OUTXR:
#if CZ80_EXACT
        /* P FLAG isn't correct here ! */
        F = SZXY[zB] + ((val >> 6) & CZ80_NF) + (val & CZ80_PF);
        if ((val + zL) & 0x100) F |= CZ80_HF | CZ80_CF;
        zF = F;
#else
        zF = SZXY[zB] + ((val >> 6) & CZ80_NF);
#endif

        if (zB)
        {
            /* instruction not yet completed... */
            /* we will continu it at next CZ80_Exec */
            zPC -= 2;
            CCnt += 4;
            goto Cz80_Check_Int;
        }

        /* instruction not yet completed... */
        RET(-(5 + 4))
    }

#if CZ80_USE_JUMPTABLE
#else
}
#endif
